package dst.ass3.jms.taskforce.impl;

import java.io.Serializable;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;

import org.apache.openejb.api.LocalClient;

import dst.ass3.dto.RateTaskWrapperDTO;
import dst.ass3.jms.Constants;
import dst.ass3.jms.taskforce.ITaskForce;
import dst.ass3.jms.taskforce.ITaskForce.ITaskForceListener.TaskWrapperDecideResponse;
import dst.ass3.jms.taskforce.ITaskForce.ITaskForceListener.TaskWrapperResponse;
import dst.ass3.model.LifecycleState;

@LocalClient
public class TaskForce implements ITaskForce{

	private final static Logger LOGGER = Logger.getLogger(TaskForce.class.getName());
	
	@Resource(name = Constants.CONNECTION_FACTORY)
	private ConnectionFactory connectionFactory;
	
	@Resource(name = Constants.TASKFORCES_QUEUE)
	private Queue taskforcesQueue;
 
 	@Resource(name = Constants.SERVER_QUEUE)
    private Queue serverQ;
	
	private final String name;
    
	private ITaskForceListener listener;

    private Connection connection;
    private Session session;
    private MessageProducer messageProducer;
    private MessageConsumer messageConsumer;

    //has a name
    public TaskForce(String name) {
        this.name = name;
    }
	
    /**
     * Lookup all the Resources
     */
	@Override
	public void start() {
		try {
            connection = connectionFactory.createConnection();
            connection.setClientID(name);
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            
            messageProducer = session.createProducer(serverQ);	//write to server
            
            messageConsumer = session.createConsumer(taskforcesQueue);	//get messages that were written in this queue from server
            messageConsumer.setMessageListener(new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    TaskForce.this.onMessage(message);
                }
            });

            connection.start();
        } catch (JMSException e) {
            e.printStackTrace();
        } 
	}
	
	/**
	 * Receive all Messages from Server and react
	 * @param message
	 */
	protected void onMessage(Message message) {
		if (message == null) {
        	LOGGER.info("Message received is null");
            return;
        }
		else{
			ObjectMessage m = (ObjectMessage)message;
	        
	        try {
	            int receivedType = m.getIntProperty(Constants.PROPERTY_MESSAGE_TYPE);
	            
	            LOGGER.info("Message type: "+receivedType+" and object "+ m.getObject());
	            
	            switch (receivedType) {
		            case Constants.MSG_SERVER_TO_TASKFORCE_DECIDE:
		            	serverDecide(m.getObject()); break;
		            default:
		            	LOGGER.info("Type received not known: "+receivedType);
	            }
	        } catch (JMSException e) {
	            e.printStackTrace();
	        }
		}
    }

    private void serverDecide(Serializable object) {
    	if (object == null) {
    		LOGGER.info("No object received");
            return;
        }
    	else if (listener == null) {
    		LOGGER.info("Listener is null");
            return;
        }
    	else{
	    	//make a new RateTaskWrapper object
	    	RateTaskWrapperDTO dto = (RateTaskWrapperDTO)object;
	
	        try {
	            TaskWrapperDecideResponse decision = listener.decideTask(dto, name);
	
	            //setRate
	            dto.setRatedBy(name);
	            
	            //differ the response decision
	            if (decision.resp == TaskWrapperResponse.ACCEPT) {
	                dto.setComplexity(decision.complexity);
	                dto.setState(LifecycleState.READY_FOR_PROCESSING);	//Processing is Possible so set State
	            } else {
	                dto.setState(LifecycleState.PROCESSING_NOT_POSSIBLE);	//Processing is not Possible
	            }
	
	            //send answer to Server
	            ObjectMessage msg = session.createObjectMessage(dto);
	            msg.setIntProperty(Constants.PROPERTY_MESSAGE_TYPE, Constants.MSG_TASKFORCE_TO_SERVER_DECISION);
	            messageProducer.send(msg);	//answer to Server
	        } catch (JMSException e) {
	            e.printStackTrace();
	        }
    	}
    }

    /**
     * stop and close all resources
     */
	@Override
	public void stop() {
		try {
			if (messageConsumer != null)
				messageConsumer.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
		try {
			if (messageProducer != null)
				messageProducer.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
		try {
			if (session != null)
				session.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
		try {
			if (connection != null)
				connection.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void setTaskForceListener(ITaskForceListener listener) {
		this.listener = listener;
	}

}
